﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Collections.Concurrent;
using System.Reflection;
using CSharpExperiments.Metaprogramming;

namespace CSharpExperiments.Scripting
{
	public class ScriptCache<T>
	{
		public Repo Repo { get; private set; }
		public Preparser Preparser { get; private set; }
		public Compiler Compiler { get; private set; }

		private ConcurrentDictionary<String, Type> loadedTypes = new ConcurrentDictionary<String, Type>();
		private Object lockObject = new Object();

		public ScriptCache(Repo repo, Preparser preparser, Compiler compiler)
		{
			Repo = repo;
			Preparser = preparser;
			Compiler = compiler;
		}

		public Type GetType(String name)
		{
			Type type;

			if (loadedTypes.TryGetValue(name, out type))
				return type;

			lock (lockObject)
			{
				if (loadedTypes.TryGetValue(name, out type))
					return type;

				String source = Repo.GetSource(name);
				ScriptInfo script = Preparser.Analyze(source);
				Assembly asm = Compiler.Compile(script);
				type = asm.GetType(script.FullTypeName);
				loadedTypes.AddOrUpdate(name, type, (n, t) => t);
				return type;
			}
		}

		public T GetInstance(String name, params Object[] args)
		{
			var type = GetType(name);
			return type.New<T>(args);
		}

		public T Deserialize(String name, String data)
		{
			var type = GetType(name);
			return type.Do<T>("Deserialize", data);
		}
	}
}
